// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package gob

import (
	"errors"
	"io"
	"reflect"
	"sync"
)

// 编码器管理类型和数据信息到
// 连接的另一侧。多个用户同时使用是安全的
// 戈罗季斯。
type Encoder struct {
	mutex      sync.Mutex              // 每个项目必须以原子方式发送
	w          []io.Writer             // 将数据发送到哪里
	sent       map[reflect.Type]typeId // 我们已经发送了哪些类型
	countState *encoderState           // 写作阶段
	freeList   *encoderState           // 免费编码器状态列表；避免重新分配
	byteBuf    encBuffer               // 顶级编码器状态的缓冲区
	err        error
}

// 在对消息进行编码之前，我们在消息头保留空间
// 在其中对其长度进行编码的缓冲区。这意味着我们可以使用
// 缓冲区，用于在不进行其他分配的情况下组装消息。
const maxLength = 9 // 编码长度的最大大小。
var spaceForLength = make([]byte, maxLength)

// NewEncoder返回将在io.Writer上传输的新编码器。
func NewEncoder(w io.Writer) *Encoder {
	enc := new(Encoder)
	enc.w = []io.Writer{w}
	enc.sent = make(map[reflect.Type]typeId)
	enc.countState = enc.newEncoderState(new(encBuffer))
	return enc
}

// writer（）返回编码器正在使用的最里面的writer
func (enc *Encoder) writer() io.Writer {
	return enc.w[len(enc.w)-1]
}

// pushWriter将写入程序添加到编码器。
func (enc *Encoder) pushWriter(w io.Writer) {
	enc.w = append(enc.w, w)
}

// popWriter弹出最里面的作者。
func (enc *Encoder) popWriter() {
	enc.w = enc.w[0 : len(enc.w)-1]
}

func (enc *Encoder) setError(err error) {
	if enc.err == nil { // 记住第一个。
		enc.err = err
	}
}

// writeMessage发送的数据项前面带有其长度的无符号计数。
func (enc *Encoder) writeMessage(w io.Writer, b *encBuffer) {
	// 已为邮件开头的长度保留了空间。
	// 这有点脏：我们从字节中获取切片。缓冲区和按摩
	// 这是手工制作的。
	message := b.Bytes()
	messageLen := len(message) - maxLength
	// 长度不能大于解码器可以处理的长度。
	if messageLen >= tooBig {
		enc.setError(errors.New("gob: encoder: message too big"))
		return
	}
	// 编码长度。
	enc.countState.b.Reset()
	enc.countState.encodeUint(uint64(messageLen))
	// 将长度复制为消息的前缀。
	offset := maxLength - enc.countState.b.Len()
	copy(message[offset:], enc.countState.b.Bytes())
	// 写下数据。
	_, err := w.Write(message[offset:])
	// 耗尽缓冲区并恢复前面的空间，以便对下一条消息进行计数。
	b.Reset()
	b.Write(spaceForLength)
	if err != nil {
		enc.setError(err)
	}
}

// sendActualType发送请求的类型，无需进一步调查，除非
// 以前有人送过。
func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTypeInfo, actual reflect.Type) (sent bool) {
	if _, alreadySent := enc.sent[actual]; alreadySent {
		return false
	}
	info, err := getTypeInfo(ut)
	if err != nil {
		enc.setError(err)
		return
	}
	// 发送配对（-id，type）
	// 身份证件：
	state.encodeInt(-int64(info.id))
	// 类型：
	enc.encode(state.b, reflect.ValueOf(info.wire), wireTypeUserInfo)
	enc.writeMessage(w, state.b)
	if enc.err != nil {
		return
	}

	// 请记住，我们已经发送了此类型，包括用户提供给我们的内容和基本类型。
	enc.sent[ut.base] = info.id
	if ut.user != ut.base {
		enc.sent[ut.user] = info.id
	}
	// 现在发送内部类型
	switch st := actual; st.Kind() {
	case reflect.Struct:
		for i := 0; i < st.NumField(); i++ {
			if isExported(st.Field(i).Name) {
				enc.sendType(w, state, st.Field(i).Type)
			}
		}
	case reflect.Array, reflect.Slice:
		enc.sendType(w, state, st.Elem())
	case reflect.Map:
		enc.sendType(w, state, st.Key())
		enc.sendType(w, state, st.Elem())
	}
	return true
}

// 如果需要，sendType会将类型信息发送到另一端。
func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) {
	ut := userType(origt)
	if ut.externalEnc != 0 {
		// 规则是不同的：不管底层类型的表示形式如何，
		// 我们需要告诉另一方，基类型是一个编码器。
		return enc.sendActualType(w, state, ut, ut.base)
	}

	// 这是一个具体的值，所以深入到基本类型。
	switch rt := ut.base; rt.Kind() {
	default:
		// 基本类型和接口不需要描述。
		return
	case reflect.Slice:
		// 如果是[]uint8，不要发送；这被认为是基本的。
		if rt.Elem().Kind() == reflect.Uint8 {
			return
		}
		// 否则我们会发送。
		break
	case reflect.Array:
		// 必须发送数组，以便我们知道它们的长度和元素类型。
		break
	case reflect.Map:
		// 必须发送地图，以便我们知道其长度和键/值类型。
		break
	case reflect.Struct:
		// 必须发送结构，以便我们知道它们的字段。
		break
	case reflect.Chan, reflect.Func:
		// 如果我们到了这里，它是一个结构的字段；别理它。
		return
	}

	return enc.sendActualType(w, state, ut, ut.base)
}

// Encode传输由空接口值表示的数据项，
// 确保所有必要的类型信息都已首先传输。
// 将nil指针传递给编码器将导致死机，因为它们无法通过gob传输。
func (enc *Encoder) Encode(e interface{}) error {
	return enc.EncodeValue(reflect.ValueOf(e))
}

// sendTypeDescriptor确保远程端知道此类型。
// 如果这是第一次创建类型，它将发送一个描述符
// 发送。
func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *userTypeInfo) {
	// 确保对方知道该类型。
	// 首先，我们已经发送了这种类型的邮件了吗？
	rt := ut.base
	if ut.externalEnc != 0 {
		rt = ut.user
	}
	if _, alreadySent := enc.sent[rt]; !alreadySent {
		// 不，那就发吧。
		sent := enc.sendType(w, state, rt)
		if enc.err != nil {
			return
		}
		// 如果类型信息仍未传输，则表示我们已发送
		// 顶层的单例基本类型（int、[]字节等）。我们没有
		// 需要发送类型信息，但我们确实需要更新enc.sent。
		if !sent {
			info, err := getTypeInfo(ut)
			if err != nil {
				enc.setError(err)
				return
			}
			enc.sent[rt] = info.id
		}
	}
}

// sendTypeId发送必须已定义的id。
func (enc *Encoder) sendTypeId(state *encoderState, ut *userTypeInfo) {
	// 标识此顶级值的类型。
	state.encodeInt(int64(enc.sent[ut.base]))
}

// EncodeValue传输由反射值表示的数据项，
// 确保所有必要的类型信息都已首先传输。
// 将nil指针传递给EncodeValue将导致死机，因为它们无法通过gob传输。
func (enc *Encoder) EncodeValue(value reflect.Value) error {
	if value.Kind() == reflect.Invalid {
		return errors.New("gob: cannot encode nil value")
	}
	if value.Kind() == reflect.Ptr && value.IsNil() {
		panic("gob: cannot encode nil pointer of type " + value.Type().String())
	}

	// 确保我们在这里是单线程的，所以是多线程的
	// goroutines可以共享一个编码器。
	enc.mutex.Lock()
	defer enc.mutex.Unlock()

	// 删除由于以前的错误而保留的所有嵌套写入程序。
	enc.w = enc.w[0:1]

	ut, err := validUserType(value.Type())
	if err != nil {
		return err
	}

	enc.err = nil
	enc.byteBuf.Reset()
	enc.byteBuf.Write(spaceForLength)
	state := enc.newEncoderState(&enc.byteBuf)

	enc.sendTypeDescriptor(enc.writer(), state, ut)
	enc.sendTypeId(state, ut)
	if enc.err != nil {
		return enc.err
	}

	// 对对象进行编码。
	enc.encode(state.b, value, ut)
	if enc.err == nil {
		enc.writeMessage(enc.writer(), state.b)
	}

	enc.freeEncoderState(state)
	return enc.err
}
